package top.i89ck.modules.service;

import java.util.List;
import java.util.Objects;

import com.jfinal.kit.Kv;
import com.jfinal.plugin.activerecord.Db;
import com.jfinal.plugin.activerecord.Page;
import com.jfinal.plugin.activerecord.SqlPara;
import top.i89ck.common.tree.TreeUtils;
import top.i89ck.modules.model.Option;
import top.i89ck.modules.utils.Const;
import top.i89ck.modules.utils.Pager;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.BooleanUtil;

/**
 * 系统权限的功能服务类， 提供权限涉及到的各种功能的具体实现方法和数据库操作方法
 *
 * @author YINGFU
 */
public class OptionService {

    /**
     * 查询系统中所有的权限列表信息
     *
     * @return
     */
    public List<Option> getAll() {
        SqlPara sqlPara = Db.getSqlPara("sysOption.getAll");
        return Option.dao.find(sqlPara);
    }

    /**
     * 按条件获取所有权限
     *
     * @param parentId
     * @param type
     * @return
     */
    private List<Option> getOptions(Long parentId, String type) {
        Kv data = Kv.by("type", type).set("parentId", parentId);
        SqlPara sqlPara = Db.getSqlPara("sysOption.getOptions", data);
        return Option.dao.find(sqlPara);
    }

    /**
     * 查询系统中左侧功能树列表的权限数据
     *
     * @return
     */
    public List<Option> getLeftMenu() {
        List<Option> options = getOptions(null, Const.DICT_OPTION_TYPE_MENU_LEFT);
        return TreeUtils.getTreeList(options);
    }

    /**
     * 通过角色获取相应的权限数据
     *
     * @param roleId
     * @param parentId
     * @param type
     * @return
     */
    private List<Option> getOptionsByRoleId(Long roleId, Long parentId, String type) {
        Kv data = Kv.by("type", type).set("roleId", roleId).set("parentId", parentId);
        SqlPara sqlPara = Db.getSqlPara("sysOption.getOptionsByRoleId", data);
        return Option.dao.find(sqlPara);
    }

    /**
     * 通过用户主键查询左侧功能树列表
     *
     * @param roleId
     * @return
     */
    public List<Option> getLeftMenuByRoleId(Long roleId) {
        List<Option> options = getOptionsByRoleId(roleId, null, Const.DICT_OPTION_TYPE_MENU_LEFT);
        return TreeUtils.getTreeList(options);
    }

    /**
     * 获取顶部消息信息权限
     *
     * @return
     */
    public List<Option> getTopMsg() {
        return getOptions(null, Const.DICT_OPTION_TYPE_MENU_TOPS);
    }

    /**
     * 通过角色主键查询当前角色挂在的顶部的消息列表信息
     *
     * @param roleId
     * @return
     */
    public List<Option> getTopMsgByRoleId(Long roleId) {
        return getOptionsByRoleId(roleId, null, Const.DICT_OPTION_TYPE_MENU_TOPS);
    }

    /**
     * 获取系统中权限的树型列表。 用于权限页面数据展示
     *
     * @return
     * @author YINGFU 2019年4月2日
     */
    public List<Option> getTreeList() {
        List<Option> options = getOptions(null, Const.DICT_OPTION_TYPE_MENU);
        return TreeUtils.getJqTreeList(options);
    }

    /**
     * 把权限的全部数据转成树型的数据模型
     *
     * @return
     * @author YINGFU 2019年4月11日
     */
    public List<Option> getTreeAll() {
        List<Option> options = getAll();
        return TreeUtils.getTreeList(options);
    }

    /**
     * 加载系统中的所有的一级菜单权限列表数据
     *
     * @return
     * @author YINGFU 2019年4月3日
     */
    public List<Option> getRoots() {
        return getOptions(Const.DEFAULT_PARENT_ID, Const.DICT_OPTION_TYPE_MENU_LEFT);
    }

    /**
     * 判断一个权限是否存在下级权限， 如果存在返回true， 否则返回false
     *
     * @param parentId
     * @return
     * @author YINGFU 2019年4月8日
     */
    public boolean hasSubOptions(Long parentId) {
        int total = getSubOptionTotal(parentId, null);
        return total > 0;
    }

    /**
     * 获取子权限的数量
     *
     * @param parentId
     * @param type
     * @return
     */
    private int getSubOptionTotal(Long parentId, String type) {
        Kv data = Kv.by("parentId", parentId).set("type", type);
        SqlPara sqlPara = Db.getSqlPara("sysOption.getSubOptionTotal", data);
        return Db.queryInt(sqlPara.getSql(), sqlPara.getPara());
    }

    /**
     * 查询并确认某个权限是否已经设置某个功能。
     *
     * @param type      功能类型
     * @param parentId 权限主键
     * @return
     * @author YINGFU 2019年4月8日
     */
    public boolean hasSubOptions(Long parentId, String type) {
        int total = getSubOptionTotal(parentId, type);
        return total > 0;
    }

    /**
     * 查询某个权限下级的某个功能分组的信息， 仅能查功能分组的信息（比如：功能按钮组名）
     *
     * @param parentId 上级权限
     * @param type     功能类型
     * @return
     */
    public Option getFirstSubOption(Long parentId, Integer type) {
        return CollUtil.getFirst(getSubOptions(parentId, type));
    }

    /**
     * 获取某个权限下的所有子权限
     *
     * @param parentId
     * @param type
     * @return
     */
    private List<Option> getSubOptions(Long parentId, Integer type) {
        Kv data = Kv.by("type", type).set("parentId", parentId);
        SqlPara sqlPara = Db.getSqlPara("sysOption.getSubOptions", data);
        List<Option> options = Option.dao.find(sqlPara);
        return options;
    }

    /**
     * 查询某个权限下级的功能分组的信息， 通过功能分组的对应的字典类型查询。
     *
     * @param dictType 字典类型
     * @param parentId 上级主键
     * @return
     */
    public Option getFirstSubOptionByDictType(String dictType, Long parentId) {
        return CollUtil.getFirst(getSubOptionsByDictType(dictType, parentId));
    }

    /**
     * 通过字典类型获取权限数据
     *
     * @param dictType
     * @param parentId
     * @return
     */
    private List<Option> getSubOptionsByDictType(String dictType, Long parentId) {
        Kv data = Kv.by("type", dictType).set("parentId", parentId);
        SqlPara sqlPara = Db.getSqlPara("sysOption.getSubOptionsByDictType", data);
        return Option.dao.find(sqlPara);
    }

    /**
     * 查询某个权限下级的功能分组的信息， 通过功能分组的对应的字典类型查询。
     *
     * @param dictType
     * @param parentId
     * @param roleId
     * @return
     */
    public Option getFirstSubOptionByRoleId(String dictType, Long parentId, Long roleId) {
        return CollUtil.getFirst(getSubOptionsByRoleIdx(roleId, parentId, dictType));
    }

    /**
     * 通过角色的主键获取所有的权限数据
     *
     * @param roleId
     * @param parentId
     * @param dictType
     * @return
     */
    private List<Option> getSubOptionsByRoleIdx(Long roleId, Long parentId, String dictType) {
        Kv data = Kv.by("type", dictType).set("parentId", parentId).set("roleId", roleId);
        SqlPara sqlPara = Db.getSqlPara("sysOption.getSubOptionsByRoleId", data);
        return Option.dao.find(sqlPara);
    }

    /**
     * 查询某个功能组名下的所有的功能数据字段信息。
     *
     * @param pager    分页
     * @param parentId 上级主键
     * @return
     * @author YINGFU 2019年4月8日
     */
    public Page<Option> getSubOptionPage(Pager pager, Long parentId) {
        Kv data = Kv.by("parentId", parentId);
        SqlPara sqlPara = Db.getSqlPara("sysOption.getOptions", data);
        return Option.dao.paginate(pager.getPageNum(), pager.getPageSize(), sqlPara);
    }

    /**
     * 通过当前的权限主键查询当前权限的前面的权限内容信息
     *
     * @param optionId 权限主键
     * @return
     */
    public Option getPreviousOption(Long optionId) {
        Kv data = Kv.by("optionId", optionId);
        SqlPara sqlPara = Db.getSqlPara("sysOption.getPreviousOption", data);
        return Option.dao.findFirst(sqlPara);
    }

    /**
     * 批量更新系统中的某些权限数据为删除状态。
     *
     * @param optionIds
     * @return
     */
    public boolean delAll(List<String> optionIds) {
        Kv data = Kv.by("optionIds", optionIds);
        SqlPara sqlPara = Db.getSqlPara("sysOption.delAll", data);
        int rows = Db.update(sqlPara);
        return rows > 0;
    }

    /**
     * 查询某个功能组下属的所有的功能数据
     *
     * @param parentIdx 上级权限主键
     * @param dictType  字典主键
     * @author YINGFU 2019年4月10日
     */
    public List<Option> getAllSubOptions(Long parentId, String dictType) {
        Kv data = Kv.by("parentId", parentId).set("type", dictType);
        SqlPara sqlPara = Db.getSqlPara("sysOption.getAllSubOptions", data);
        return Option.dao.find(sqlPara);
    }

    /**
     * 根据角色主键查询这个角色下的某个功能组的所有的功能按钮
     *
     * @param optionId
     * @param type
     * @param roleId
     * @return
     */
    public List<Option> getAllSubOptionsByRoleId(Long optionId, String type, Long roleId) {
        Kv data = Kv.by("optionId", optionId).set("type", type).set("roleId", roleId);
        SqlPara sqlPara = Db.getSqlPara("sysOption.getAllSubOptionsByRoleId", data);
        List<Option> options = Option.dao.find(sqlPara);
        return options;
    }

    /**
     * 通过权限的主键查询某个权限是否是某种类型的权限， 如果是， 返回这个权限，否则返回null
     *
     * @param dictType
     * @param optionId
     * @return
     */
    public Option getOptionByDictType(String dictType, Long optionId) {
        return CollUtil.getFirst(getOptionsByDictType(dictType, optionId));
    }

    /**
     * 通过字典的类型获取权限的数据
     *
     * @param dictType
     * @param optionId
     * @return
     */
    private List<Option> getOptionsByDictType(String dictType, Long optionId) {
        Kv data = Kv.by("type", dictType).set("id", optionId);
        SqlPara sqlPara = Db.getSqlPara("sysOption.getOptionsByDictType", data);
        return Option.dao.find(sqlPara);
    }

    /**
     * 通过连接地址 ，查询权限中配置的菜单地址
     *
     * @param url
     * @return
     */
    public Option getOptionByUrl(String url) {
        Kv data = Kv.by("url", url);
        SqlPara sqlPara = Db.getSqlPara("sysOption.getOptionByUrl", data);
        return Option.dao.findFirst(sqlPara);
    }

    /**
     * 通过主键获取权限的信息
     *
     * @param optionId
     * @return
     */
    public Option findById(Long optionId) {
        return Option.dao.findById(optionId);
    }

    /**
     * 新增/修改权限数据
     *
     * @param option
     * @return
     */
    public boolean save(Option option) {
        option.setShrinkToFit(BooleanUtil.isTrue(option.getShrinkToFit()));
        option.setKey(BooleanUtil.isTrue(option.getKey()));
        option.setHidden(BooleanUtil.isTrue(option.getHidden()));
        if (Objects.isNull(option.getId())) {
            option.setSort(System.currentTimeMillis());
            option.setDeleted(false);
            return option.save();
        } else {
            Option entity = findById(option.getId());
            if (Objects.isNull(entity)) {
                option.setSort(System.currentTimeMillis());
                option.setDeleted(false);
                return option.save();
            } else {
                return option.update();
            }
        }
    }

    /**
     * 向上移动一位
     *
     * @return
     */
    public boolean moveUp(Option previous, Long optionId) {
        return Db.tx(() -> {
            Option option = findById(optionId);
            long tmpLastSort = previous.getSort();
            long tmpThisSort = option.getSort();

            Option beforeOption = new Option();
            beforeOption.setSort(tmpLastSort);
            beforeOption.setId(option.getId());

            Option afterOption = new Option();
            afterOption.setSort(tmpThisSort);
            afterOption.setId(previous.getId());

            boolean flag = beforeOption.update();
            if (flag) {
                flag = afterOption.update();
            }
            return flag;
        });
    }

}
